//
// PicoMax
// Copyright 2012 Thomas W. Holtquist
// www.skewworks.com
//
// GPL 3.0 Licensed
//

using System;
using Microsoft.SPOT;

namespace Skewworks.PicoMax
{

    /// <summary>
    /// Structure containing the X and Y of a specific point
    /// </summary>
    [Serializable]
    public struct Point
    {

        #region Variables

        /// <summary>
        /// X location of the point
        /// </summary>
        public int X;

        /// <summary>
        /// Y location of the point
        /// </summary>
        public int Y;

        #endregion

        #region Constructor

        /// <summary>
        /// Creates a new point
        /// </summary>
        /// <param name="X">X Location</param>
        /// <param name="Y">Y Location</param>
        public Point(int X, int Y)
        {
            this.X = X;
            this.Y = Y;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns a string representation of the Point
        /// </summary>
        /// <returns>{X, Y}</returns>
        public override string ToString()
        {
            return "{" + X + ", " + Y + "}";
        }

        #endregion

    }

    /// <summary>
    /// Structure defining a Rectange
    /// </summary>
    [Serializable]
    public struct Rect
    {

        #region Variables

        private int x;
        private int y;
        private int w;
        private int h;

        #endregion

        #region Constructor

        /// <summary>
        /// Creates a new Rect
        /// </summary>
        /// <param name="X">X location of Rect</param>
        /// <param name="Y">Y location of Rect</param>
        /// <param name="Width">Width of Rect</param>
        /// <param name="Height">Height of Rect</param>
        public Rect(int X, int Y, int Width, int Height)
        {
            this.x = X;
            this.y = Y;
            this.w = Width;
            this.h = Height;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets/Sets X location of Rect
        /// </summary>
        public int X
        {
            get { return x; }
            set { x = value; }
        }

        /// <summary>
        /// Gets/Sets Y location of Rect
        /// </summary>
        public int Y
        {
            get { return y; }
            set { y = value; }
        }

        /// <summary>
        /// Gets/Sets Width of Rect
        /// </summary>
        public int Width
        {
            get { return w; }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("Width cannot be less than 0");
                w = value;
            }
        }

        /// <summary>
        /// Gets/Sets Height of Rect
        /// </summary>
        public int Height
        {
            get { return h; }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("Height cannot be less than 0");
                h = value;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds the area of a second Rect to current Rect
        /// </summary>
        /// <param name="newRect">Rect to add</param>
        public void Combine(Rect newRect)
        {
            if (w == 0)
            {
                x = newRect.X;
                y = newRect.Y;
                w = newRect.Width;
                h = newRect.Height;
                return;
            }

            int x1, y1, x2, y2;
            x1 = (x < newRect.X) ? x : newRect.X;
            y1 = (y < newRect.Y) ? y : newRect.Y;
            x2 = (x + this.Width > newRect.X + newRect.Width) ? x + w : newRect.X + newRect.Width;
            y2 = (y + this.Height > newRect.Y + newRect.Height) ? y + h : newRect.Y + newRect.Height;
            x = x1;
            y = y1;
            w = x2 - x1;
            h = y2 - y1;
        }

        /// <summary>
        /// Returns the combination of two Rects
        /// </summary>
        /// <param name="region1">Rect 1</param>
        /// <param name="region2">Rect 2</param>
        /// <returns>Combined Rect</returns>
        public Rect Combine(Rect region1, Rect region2)
        {
            if (region1.Width == 0)
                return region2;
            if (region2.Width == 0)
                return region1;

            int x1, y1, x2, y2;
            x1 = (region1.X < region2.X) ? region1.X : region2.X;
            y1 = (region1.Y < region2.Y) ? region1.Y : region2.Y;
            x2 = (region1.X + region1.Width > region2.X + region2.Width) ? region1.X + region1.Width : region2.X + region2.Width;
            y2 = (region1.Y + region1.Height > region2.Y + region2.Height) ? region1.Y + region1.Height : region2.Y + region2.Height;
            return new Rect(x1, y1, x2 - x1, y2 - y1);
        }

        /// <summary>
        /// Checks if a point is inside the Rect
        /// </summary>
        /// <param name="X">X location</param>
        /// <param name="Y">Y location</param>
        /// <returns>True if point is inside Rect</returns>
        public bool Contains(int X, int Y)
        {
            if (X >= x && X <= x + w && Y >= y && Y <= y + h)
                return true;
            return false;
        }

        /// <summary>
        /// Checks if a Point is inside the Rect
        /// </summary>
        /// <param name="e">Point to check</param>
        /// <returns>True if Point is inside Rect</returns>
        public bool Contains(Point e)
        {
            if (e.X >= x && e.X <= x + w && e.Y >= y && e.Y <= y + h) return true;
            return false;
        }

        /// <summary>
        /// Checks to see if two Rects interset
        /// </summary>
        /// <param name="area">Rect to check</param>
        /// <returns>True if Rects intersect</returns>
        public bool Intersects(Rect area)
        {
            return !(area.X >= (x + w)
                    || (area.X + area.Width) <= x
                    || area.Y >= (y + h)
                    || (area.Y + area.Height) <= y
                    );
        }

        /// <summary>
        /// Returns the intersection of two Rects
        /// </summary>
        /// <param name="region1">Rect 1</param>
        /// <param name="region2">Rect 2</param>
        /// <returns>Intersected Rect</returns>
        public static Rect Intersect(Rect region1, Rect region2)
        {
            if (!region1.Intersects(region2))
                return new Rect(0, 0, 0, 0);

            Rect rct = new Rect();

            // For X1 & Y1 we'll want the highest value
            rct.X = (region1.X > region2.X) ? region1.X : region2.X;
            rct.Y = (region1.Y > region2.Y) ? region1.Y : region2.Y;

            // For X2 & Y2 we'll want the lowest value
            int r1V2 = region1.X + region1.Width;
            int r2V2 = region2.X + region2.Width;
            rct.Width = (r1V2 < r2V2) ? r1V2 - rct.X : r2V2 - rct.X;
            r1V2 = region1.Y + region1.Height;
            r2V2 = region2.Y + region2.Height;
            rct.Height = (r1V2 < r2V2) ? r1V2 - rct.Y : r2V2 - rct.Y;

            return rct;
        }

        /// <summary>
        /// Returns a string representation of the Rect
        /// </summary>
        /// <returns>{X, Y, Width, Height}</returns>
        public override string ToString()
        {
            return "{" + this.X + ", " + this.Y + ", " + this.Width + ", " + this.Height + "}";
        }

        #endregion

    }

    /// <summary>
    /// Red/Blue/Green structure in 565 mode
    /// </summary>
    [Serializable]
    public struct RGB565
    {

        #region Variables

        /// <summary>
        /// Red value
        /// </summary>
        public byte Red;

        /// <summary>
        /// Green Value
        /// </summary>
        public byte Green;

        /// <summary>
        /// Blue Value
        /// </summary>
        public byte Blue;

        /// <summary>
        /// 565 Value
        /// </summary>
        public ushort Value;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new RGB565 by component values
        /// </summary>
        /// <param name="Red"></param>
        /// <param name="Green"></param>
        /// <param name="Blue"></param>
        public RGB565(byte Red, byte Green, byte Blue)
        {
            this.Red = Red;
            this.Green = Green;
            this.Blue = Blue;
            this.Value = (ushort)((Blue >> 3) | ((Green & 0xFC) << 3) | ((Red & 0xF8) << 8));
        }

        /// <summary>
        /// Creates a new RGB565 by 565 value
        /// </summary>
        /// <param name="Value"></param>
        public RGB565(ushort Value)
        {
            this.Blue = (byte)(Value << 3);
            this.Green = (byte)(Value >> 5 << 2);
            this.Red = (byte)(Value >> 11 << 3);
            this.Value = Value;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns a string representation of the RGB565 color
        /// </summary>
        /// <returns>{R, G, B}</returns>
        public string ToString()
        {
            return "{" + this.Red + ", " + this.Green + ", " + this.Blue + "}";
        }

        #endregion

    }


    /// <summary>
    /// Structure containing object Height & Width
    /// </summary>
    [Serializable]
    public struct Size
    {

        #region Variables

        /// <summary>
        /// Width of the object
        /// </summary>
        public int Width;

        /// <summary>
        /// Height of the object
        /// </summary>
        public int Height;

        #endregion

        #region Constructor

        /// <summary>
        /// Creates a new Size
        /// </summary>
        /// <param name="Width"></param>
        /// <param name="Height"></param>
        public Size(int Width, int Height)
        {
            this.Width = Width;
            this.Height = Height;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds Height & Width to existing size
        /// </summary>
        /// <param name="AddWidth"></param>
        /// <param name="AddHeight"></param>
        public void Grow(int AddWidth, int AddHeight)
        {
            Width += AddWidth;
            Height += AddHeight;
        }

        /// <summary>
        /// Subtracts Height & Width from existing size
        /// </summary>
        /// <param name="SubtractWidth"></param>
        /// <param name="SubtractHeight"></param>
        public void Shrink(int SubtractWidth, int SubtractHeight)
        {
            Width += SubtractWidth;
            if (Width < 0)
                Width = 0;
            Height += SubtractHeight;
            if (Height < 0)
                Height = 0;
        }

        /// <summary>
        /// Returns a string representation of the Size
        /// </summary>
        /// <returns>{Width, Height}</returns>
        public string ToString()
        {
            return "{" + this.Width + ", " + this.Height + "}";
        }

        #endregion

    }

}
